#pragma once

#include <stdexcept>
#include <iostream>
#include <vector>
#include <mutex>
#include <cassert>
#include <cstring>
#include <cstdint>
#include <unistd.h>
#include <sys/mman.h>


// 整体控制在10%的左右的内碎片浪费
//  size范围               对齐数             对应的哈希桶下标范围
//  [1,128]                8B 对齐            freelist[0,16)
//  [128+1,1024]           16B 对齐           freelist[16,72)
//  [1024+1,8*1024]        128B 对齐          freelist[72,128)
//  [8*1024+1,64*1024]     1024B 对齐         freelist[128,184)
//  [64*1024+1,256*1024]   8*1024 对齐        freelist[184,208)

const static int FREE_LIST_NUM = 208;    // 自由链表的最大个数
const static int MAX_BYTES = 256 * 1024; // 最大字节数
const static int PAGE_NUM = 129;         // 最大管理页数
                          /*这里给129的原因是，因为映射时需要减少一个，
                            那这样的话后续操作都要进行-1来完成映射
                            而这里给129就可以直接进行一一对应的操作*/
const static int PAGE_SHIFT = 13;        // 页的偏移量
                          /*13就是8kb为一页大小*/
typedef size_t PageID; // 因为size_t的大小会根据系统多少位而自动发生变化：32位下为4字节，64位下为8字节，因此不需要其余的修改

static inline void *SystemAlloc(size_t kpage)
{
    //  cout << "debug SystemAlloc begin" << endl;
    void *ptr = nullptr;

    size_t total_size = kpage << PAGE_SHIFT; // 获得总空间大小
    if (total_size >= 128 * 1024)    // 如果申请的空间大于128kb，则用mmap()
    {
        ptr = mmap(nullptr,                     /*当我们使用nullptr的时候，系统会自动选择位置*/
                   total_size,                  /*申请的空间大小*/
                   PROT_READ | PROT_WRITE,      /*这段被申请的空间可以读，也可以被写*/
                   MAP_PRIVATE | MAP_ANONYMOUS, /*私有匿名映射*/
                   -1, 0);                      /*当flag中使用了MAP_ANONYMOUS这个宏，则fd需要被设置为-1，offset应该被设置为0*/
        if (ptr == MAP_FAILED)                  // 该函数错误时，返回MAP_FAILED，不是nullptr
        {
            ptr = nullptr;
        }
    }
    else//申请的空间小于128kb，此时使用brk()来申请空间
    {
        void* current_ptr = sbrk(0);//获取当前的program break
        if(sbrk(total_size) == (void*)-1)//因为sbrk的返回值是返回之前的program break，所以这里其实相当于就是current_ptr申请了total_size空间
                                         //换句话说，sbrk的作用其实是在原有空间上向后拓展空间
        {
            ptr = nullptr;
        }
        else
        {
            ptr = current_ptr;
        }
    }

    if(ptr == nullptr)
        throw std::bad_alloc();//抛出异常

    // cout << "debug SystemAlloc end && ptr: " << ptr << endl;
    return ptr;
}

static inline void SystemFree(void* ptr, size_t kpage)
{
    assert(ptr);//ptr一定是要空间的

    size_t total_size = kpage << PAGE_SHIFT;//计算出总空间大小

    if(munmap(ptr, total_size) == -1)//调用munmap来回收对应的空间
    {
        throw std::bad_alloc();//抛异常
    }
}